package common

import (
	"errors"
	"github.com/golang-jwt/jwt"
	"github.com/spf13/viper"
	"time"
)

/**
 * @Author: zhaobin
 * @Description jwt认证
 * @Date: 2022-04-26 11:15
 */

//配置文件中自己配置的
var mySecret = []byte("夏天夏天悄悄过去")

// MyClaims 自定义声明结构体并内嵌jwt.StandardClaims
// jwt包自带的jwt.StandardClaims只包含了官方字段
// 我们这里需要额外记录一个username字段，所以要自定义结构体
// 如果想要保存更多信息，都可以添加到这个结构体中
type MyClaims struct {
	UserID   int64  `json:"user_id"`
	Username string `json:"username"`
	jwt.StandardClaims
}

// GenToken 根据id和username生成JWT
func GenToken(userID int64, username string) (string, error) {
	// 创建一个我们自己的声明
	c := MyClaims{
		userID,
		username, // 自定义字段
		jwt.StandardClaims{
			ExpiresAt: time.Now().Add(time.Duration(viper.GetInt("auth.jwt_expire")) * time.Hour).Unix(), // 过期时间
			Issuer:    "my-project",                                                                      // 签发人
		},
	}
	// 使用指定的签名方法创建签名对象
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
	// 使用指定的secret签名并获得完整的编码后的字符串token
	return token.SignedString(mySecret)
}

// ParseToken 根据传入的token值获取到MyClaims对象信息(进而获取其中的id和密码)
func ParseToken(tokenString string) (*MyClaims, error) {
	// 解析token，用于解析鉴权的声明，方法内部主要是具体的解码和校验的过程，最终返回*Token
	token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (i interface{}, err error) {
		return mySecret, nil
	})
	if err != nil {
		return nil, err
	}
	// 从tokenClaims中获取到Claims对象，并使用断言，将该对象转换为我们自己定义的Claims
	// 要传入指针，项目结构体都是用指针传递，节省空间
	if claims, ok := token.Claims.(*MyClaims); ok && token.Valid { // 校验token
		return claims, nil
	}
	return nil, errors.New("invalid token")
}

//// ParseToken2 解析JWT的另外一种写法
//func ParseToken2(tokenString string) (*MyClaims, error) {
//	// 解析token
//	var mc = new(MyClaims)
//	//解析token
//	token, err := jwt.ParseWithClaims(tokenString, mc, func(token *jwt.Token) (i interface{}, err error) {
//		return mySecret, nil
//	})
//	if err != nil {
//		return nil, err
//	}
//	if token.Valid { // 校验token
//		return mc, nil
//	}
//	return nil, errors.New("invalid token")
//}
